/*-------------------------------------------------------------------------
 *
 * extensible.h
 *	  可扩展节点和自定义扫描的定义
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/nodes/extensible.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef EXTENSIBLE_H
#define EXTENSIBLE_H

#include "access/parallel.h"
#include "commands/explain.h"
#include "nodes/execnodes.h"
#include "nodes/pathnodes.h"
#include "nodes/plannodes.h"

/* 可扩展节点标识符的最大长度 */
#define EXTNODENAME_MAX_LEN					64

/*
 * 可扩展节点是由扩展定义的新类型节点。该类型始终为 T_ExtensibleNode，
 * 而 extnodename 用于识别特定类型的节点。可以查找 extnodename 以找到
 * 此节点类型的 ExtensibleNodeMethods。
 */
typedef struct ExtensibleNode
{
	NodeTag		type;
	const char *extnodename;	/* ExtensibleNodeMethods 的标识符 */
} ExtensibleNode;

/*
 * node_size 是此类型的可扩展节点的字节大小。
 *
 * nodeCopy 是一个函数，它执行从 oldnode 到 newnode 的深拷贝。
 * 它不需要拷贝类型或 extnodename，这些由核心系统复制。
 *
 * nodeEqual 是一个函数，它执行 a 和 b 之间的深度相等比较，并相应地
 * 返回 true 或 false。它不需要比较类型或 extnodename，这些由核心系统比较。
 *
 * nodeOut 是该节点类型的序列化函数。它应使用 outfuncs.c 中典型的
 * 输出约定。它不需要输出类型或 extnodename；核心系统处理这些。
 *
 * nodeRead 是该节点类型的反序列化函数。它不需要读取类型或
 * extnodename；核心系统处理这些。它应该使用 pg_strtok() 从当前输入流获取
 * 下一个令牌，然后根据 readfuncs.c 中的方式重建私有字段。
 *
 * 所有回调都是必需的。
 */
typedef struct ExtensibleNodeMethods
{
	const char *extnodename;
	Size		node_size;
	void		(*nodeCopy) (struct ExtensibleNode *newnode,
							 const struct ExtensibleNode *oldnode);
	bool		(*nodeEqual) (const struct ExtensibleNode *a,
							  const struct ExtensibleNode *b);
	void		(*nodeOut) (struct StringInfoData *str,
							const struct ExtensibleNode *node);
	void		(*nodeRead) (struct ExtensibleNode *node);
} ExtensibleNodeMethods;

extern void RegisterExtensibleNodeMethods(const ExtensibleNodeMethods *method);
extern const ExtensibleNodeMethods *GetExtensibleNodeMethods(const char *name,
															 bool missing_ok);

/*
 * 自定义路径的标志，指示生成的扫描具有什么能力。
 * CustomPath 和 CustomScan 节点的标志字段是这些标志的位掩码。
 */
#define CUSTOMPATH_SUPPORT_BACKWARD_SCAN	0x0001
#define CUSTOMPATH_SUPPORT_MARK_RESTORE		0x0002
#define CUSTOMPATH_SUPPORT_PROJECTION		0x0004

/*
 * 自定义路径方法。大多数情况下，我们只需要知道如何将
 * CustomPath 转换为计划。
 */
typedef struct CustomPathMethods
{
	const char *CustomName;

	/* 将路径转换为计划 */
	struct Plan *(*PlanCustomPath) (PlannerInfo *root,
									RelOptInfo *rel,
									struct CustomPath *best_path,
									List *tlist,
									List *clauses,
									List *custom_plans);
	struct List *(*ReparameterizeCustomPathByChild) (PlannerInfo *root,
													 List *custom_private,
													 RelOptInfo *child_rel);
}			CustomPathMethods;

/*
 * 自定义扫描。在这里，同样，事情不多：我们需要能够
 * 生成与扫描对应的 ScanState。
 */
typedef struct CustomScanMethods
{
	const char *CustomName;

	/* 从 CustomScan 计划节点创建执行状态 (CustomScanState) */
	Node	   *(*CreateCustomScanState) (CustomScan *cscan);
} CustomScanMethods;

/*
 * CustomScanState 的执行时间方法。这比我们对自定义路径或扫描
 * 所需的要复杂得多。
 */
typedef struct CustomExecMethods
{
	const char *CustomName;

	/* 必需的执行器方法 */
	void		(*BeginCustomScan) (CustomScanState *node,
									EState *estate,
									int eflags);
	TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);
	void		(*EndCustomScan) (CustomScanState *node);
	void		(*ReScanCustomScan) (CustomScanState *node);

	/* 可选方法：如果支持标记/恢复则需要 */
	void		(*MarkPosCustomScan) (CustomScanState *node);
	void		(*RestrPosCustomScan) (CustomScanState *node);

	/* 可选方法：如果支持并行执行则需要 */
	Size		(*EstimateDSMCustomScan) (CustomScanState *node,
										  ParallelContext *pcxt);
	void		(*InitializeDSMCustomScan) (CustomScanState *node,
											ParallelContext *pcxt,
											void *coordinate);
	void		(*ReInitializeDSMCustomScan) (CustomScanState *node,
											  ParallelContext *pcxt,
											  void *coordinate);
	void		(*InitializeWorkerCustomScan) (CustomScanState *node,
											   shm_toc *toc,
											   void *coordinate);
	void		(*ShutdownCustomScan) (CustomScanState *node);

	/* 可选：在 EXPLAIN 中打印额外信息 */
	void		(*ExplainCustomScan) (CustomScanState *node,
									  List *ancestors,
									  ExplainState *es);
} CustomExecMethods;

extern void RegisterCustomScanMethods(const CustomScanMethods *methods);
extern const CustomScanMethods *GetCustomScanMethods(const char *CustomName,
													 bool missing_ok);

#endif							/* EXTENSIBLE_H */
